package com.dev;
import android.graphics.Point;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import com.log;
import java.util.LinkedList;
public abstract class MyListView extends ViewGroup {
	int position;
	int childScroll;
	int childScrollMax;
    int defaultItemHeight;
	MainActivity ma;
	LinkedList<View> excess=new LinkedList<View>();
	boolean inertia;
    boolean isChildCanScroll;
	public MyListView(MainActivity ma){
		super(ma);
		this.ma=ma;
		fingerMovedLen=(int)(4*ma.dp);
		setLayerType(LAYER_TYPE_HARDWARE,null);
        defaultItemHeight=ma.dialogTitleBarHeight;
	}
    public int getItemHeight(int position){
        return defaultItemHeight;
    }
	public void letScroll(int scroll){
        if(isChildCanScroll){
            childScroll=scroll;
            for(int index=0,count=getChildCount();index<count;index++){
                View child=getChildAt(index);
                child.scrollTo(scroll,0);
            }
        }
		
	}
	public View getSelectedView(){
		return null;
	}
	public final void atDataInvalidate() {
		position=0;
		setScrollY(0);
        vw=getWidth();
        vh=getHeight();
		int vs=getChildCount();
		int ds=getCount();
        int index;
        int yy=0;
		preventLayout=true;
		for(index=0;;index++){
			if(index<vs){
				if(index<ds){
					if(yy<vh){
						View v=getChildAt(index);
						View got=getView(v,position+index);
						//measure(got);
						int mw=vw;//got.getMeasuredWidth();
						int mh=defaultItemHeight;//got.getMeasuredHeight();
                        if(got!=v){
                            removeViewsInLayout(index,1);
                            addView(got,index);
                            excess.add(v);
                            got.layout(0,yy,mw,yy+mh);
                        }else{
                            got.layout(0,yy,mw,yy+mh);
                        }
						
						yy+=mh;
					}else{
						/*for(;index<vs;vs--){
							View v=getChildAt(index);
							removeViewsInLayout(index,1);
							excess.add(v);
						}*/
						break;
					}
				}else{
					for(;index<vs;vs--){
						View v=getChildAt(index);
						removeViewsInLayout(index,1);
						excess.add(v);
					}
					break;
				}
			}else{
				for(;;index++){
					if(index<ds){
						if(yy<vh){
							View got;
							if(excess.isEmpty()){
								got=getView(null,position+index);
							}else{
								got=getView(excess.pop(),position+index);
							}
							addViewInLayout(got,index,got.getLayoutParams());
							//measure(got);
							int mw=vw;got.getMeasuredWidth();
							int mh=defaultItemHeight;//got.getMeasuredHeight();
							got.layout(0,yy,mw,yy+mh);
							vs++;
						}else{
							break;
						}
					}else{
						break;
					}
				}
				break;
			}
		}
		preventLayout=false;
	}
	public final void atDataChanged(){
		int vs=getChildCount();
		int ds=getCount();
		if(position>=ds){
			position=ds-1;
		}
		int scrollYWill=0;
		if(vs>0){
			View v=getChildAt(0);
			scrollYWill=-(v.getTop()-getScrollY());
		}
        int index=0,yy=0;
		for(;;index++){
			if(index<vs){
				if(position+index<ds){
					if(yy<scrollYWill+vh){
						View v=getChildAt(index);
						View got=getView(v,position+index);
						measure(got);
						int mw=got.getMeasuredWidth();
						int mh=got.getMeasuredHeight();
                        if(got==v){
                            got.layout(0,yy,mw,yy+mh);
                            got.setVisibility(VISIBLE);
                        }else{
                            removeViewsInLayout(index,1);
                            addViewInLayout(got,index,got.getLayoutParams(),true);
                            got.layout(0,yy,mw,yy+mh);
                        }
						yy+=mh;
					}else{
						for(;index<vs;vs--){
							View v=getChildAt(index);
                            v.setVisibility(GONE);
							///removeViewsInLayout(index,1);
							//excess.add(v);
						}
						break;
					}
				}else if(yy<scrollYWill+vh){
					if(yy<vh){
						scrollYWill=0;
					}else{
						scrollYWill=yy-vh;
					}
					for(;index<vs;vs--){
						View v=getChildAt(index);
                        v.setVisibility(GONE);
						//removeViewsInLayout(index,1);
						//excess.add(v);
					}
					break;
				}else{
					for(;index<vs;vs--){
						View v=getChildAt(index);
                        v.setVisibility(GONE);
						//removeViewsInLayout(index,1);
						//excess.add(v);
					}
					break;
				}
			}else{
				for(;;index++){
					if(position+index<ds){
						if(yy<scrollYWill+vh){
							View got;
							if(excess.isEmpty()){
								got=getView(null,position+index);
							}else{
								got=getView(excess.pop(),position+index);
							}
							addViewInLayout(got,index,got.getLayoutParams(),true);
							measure(got);
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							got.layout(0,yy,mw,yy+mh);
							vs++;
						}else{
							break;
						}
					}else if(yy<scrollYWill+vh){
						if(yy<vh){
							scrollYWill=0;
						}else{
							scrollYWill=yy-vh;
						}
						break;
					}else{
						break;
					}
				}
				break;
			}
		}
		setScrollY(scrollYWill);
        log.alert("the index is from 0 to "+index);
	}
	void measure(View view){
		LayoutParams lp=view.getLayoutParams();
		if(lp==null){
			view.measure(MeasureSpec.makeMeasureSpec(vw,MeasureSpec.AT_MOST),MeasureSpec.makeMeasureSpec(vh,MeasureSpec.AT_MOST));
		}else{
			int ww,hh;
			if(lp.width==LayoutParams.WRAP_CONTENT){
				ww=MeasureSpec.makeMeasureSpec(vw,MeasureSpec.AT_MOST);
			}else if(lp.width==LayoutParams.MATCH_PARENT){
				ww=MeasureSpec.makeMeasureSpec(vw,MeasureSpec.EXACTLY);
			}else{
				ww=MeasureSpec.makeMeasureSpec(lp.width,MeasureSpec.EXACTLY);
			}
			if(lp.height==LayoutParams.WRAP_CONTENT){
				hh=MeasureSpec.makeMeasureSpec(vh,MeasureSpec.AT_MOST);
			}else if(lp.height==LayoutParams.MATCH_PARENT){
				hh=MeasureSpec.makeMeasureSpec(vh,MeasureSpec.EXACTLY);
			}else{
				hh=MeasureSpec.makeMeasureSpec(lp.height,MeasureSpec.EXACTLY);
			}
			view.measure(ww,hh);
			if(view.getMeasuredHeight()>=vh){log.alert("a view too height");}
		}
	}
	public abstract int getCount();
	public abstract View getView(View convert, int position);
	protected int vw,vh;
	protected volatile boolean preventLayout;
	@Override
	protected void onLayout(boolean p, int ll, int tt, int rr, int bb) {
		if(touched)return;
		if(preventLayout)return;
		int ds=getCount();
		int vs=getChildCount();
		if(position>=ds)position=ds-1;
		position=position<0?0:position;
		vw=rr-ll;
		vh=bb-tt;
		childScrollMax=vw;
		int scrollYWill=0;
		if(vs>0){
			View v=getChildAt(0);
			scrollYWill=-(v.getTop()-getScrollY());
		}
		for(int index=0,yy=0;;index++){
			if(index<vs){
				if(position+index<ds){
					if(yy<vh){
						View v=getChildAt(index);
						View got=getView(v,position+index);
						measure(got);
						int mw=got.getMeasuredWidth();
						int mh=got.getMeasuredHeight();
						removeViewsInLayout(index,1);
						addViewInLayout(got,index,got.getLayoutParams(),true);
						got.layout(0,yy,mw,yy+mh);
						yy+=mh;
					}else{
						for(;index<vs;vs--){
							View v=getChildAt(index);
							excess.add(v);
							removeViewInLayout(v);
						}
						break;
					}
				}else if(yy<scrollYWill+vh){
					if(yy<vh){
						scrollYWill=0;
					}else{
						scrollYWill=yy-vh;
					}
					for(;index<vs;vs--){
						View v=getChildAt(index);
						removeViewsInLayout(index,1);
						excess.add(v);
					}
					break;
				}else{
					for(;index<vs;vs--){
						View v=getChildAt(index);
						removeViewsInLayout(index,1);
						excess.add(v);
					}
					break;
				}
			}else{
				for(;;index++){
					if(position+index<ds){
						if(yy<vh){
							View got;
							if(excess.isEmpty()){
								got=getView(null,position+index);
							}else{
								got=getView(excess.pop(),position+index);
							}
							measure(got);
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							addViewInLayout(got,index,got.getLayoutParams(),true);
							got.layout(0,yy,mw,yy+mh);
							yy+=mh;
						}else{
							break;
						}
					}else if(yy<scrollYWill+vh){
						if(yy<vh){
							scrollYWill=0;
						}else{
							scrollYWill=yy-vh;
						}
						break;
					}else{
						break;
					}
				}
				break;
			}
		}
		setScrollY(scrollYWill);
		/*for (int index=0,count=getChildCount();index < count;index++) {
			View child=getChildAt(index);
			child.setTop(yy);
			child.setBottom(yy + child.getMeasuredHeight());
			child.setLeft(0);
			child.setRight(child.getMeasuredWidth());
			yy += child.getMeasuredHeight();
		}*/
	}
	boolean fingerMoved;
	volatile boolean scrolling;
	Point touchDown=new Point();
	Point touchSaved=new Point();
	int fingerMovedLen;
	int scrollYSaved;
	int scrollXSaved;
	int childScrollSaved;
	int fingerMovedOrientation;
	volatile int inertiaLim;
	double speed;
	long recentTouchTime;
	Point recentTouch=new Point();
	volatile boolean touched;
	volatile InertiaScroll is;
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		int ex=(int)ev.getX();
		int ey=(int)ev.getY();
		switch(ev.getAction()){
			case MotionEvent.ACTION_DOWN:
				touchDown.x=ex;
				touchDown.y=ey;
				scrolling=false;
				scrollYSaved=getScrollY();
				scrollXSaved=getScrollX();
				childScrollSaved=childScroll;
				touchSaved.x=ex;
				touchSaved.y=ey;
				fingerMoved=false;
				touched=true;
				preventLayout=true;
				if(is!=null)is.interrupt();
				super.dispatchTouchEvent(ev);
				break;
			case MotionEvent.ACTION_MOVE:
				if(fingerMoved){
					int dx=ex-touchSaved.x;
					int dy=ey-touchSaved.y;
					int scrollYWill=scrollYSaved-dy;
					if(scrolling){
						long dTime=System.currentTimeMillis()-recentTouchTime;
						if(dTime>0){
							double fingerMovedSpeed_will=(ey-recentTouch.y)/dTime;
							speed=(fingerMovedSpeed_will+speed)/2;
						}
						int sy=willScroll(scrollYWill);
						if(sy!=scrollYWill){
							scrollYSaved=sy;
							touchSaved.x=ex;
							touchSaved.y=ey;
						}
					}else{
						int scrollXWill=scrollXSaved-dx;
						if(scrollXWill>=0){
							setScrollX(scrollXWill);
						}else{
							setScrollX(0);
						}
						/*int childScrollWill=childScrollSaved-dx;
						if(childScrollWill>=0){
							if(childScrollWill<=childScrollMax){
								letScroll(childScrollWill);
							}else{
								letScroll(childScrollMax);
							}
						}else{
							letScroll(0);
						}*/
					}
				}else{
					int dx=ex-touchDown.x;
					int dy=ey-touchDown.y;
					int dxabs=Math.abs(dx);
					int dyabs=Math.abs(dy);
					if(dxabs>fingerMovedLen||dyabs>fingerMovedLen){
						fingerMoved=true;
						ev.setAction(MotionEvent.ACTION_CANCEL);
						super.dispatchTouchEvent(ev);
						long dTime=System.currentTimeMillis()-ev.getDownTime();
						if(dxabs<dyabs){
							scrolling=true;
							fingerMovedOrientation=1;
							if(dTime>0){
								speed=(ey-touchDown.y)/dTime;
							}
						}else{
							fingerMovedOrientation=0;
							if(dTime>0){
								speed=(ex-touchDown.x)/dTime;
							}
						}
					}
				}
				recentTouchTime=System.currentTimeMillis();
				recentTouch.x=ex;
				recentTouch.y=ey;
				break;
			case MotionEvent.ACTION_UP:
				touched=false;
				if(fingerMoved){
					if(scrolling){
						if(inertia){
							long dTime=System.currentTimeMillis()-recentTouchTime;
							if(dTime>4){
								float fingerMovedSpeed_will=(ey-recentTouch.y)/dTime;
								speed=(fingerMovedSpeed_will+speed)/2;
							}
							if(inertiaLim==0){
								is=new InertiaScroll(speed);
								is.orientation=1;
								is.initialScrollY=getScrollY();
								is.initialScrollX=getScrollX();
								is.start();
							}
						}
					}else{
						preventLayout=false;
					}
				}else{
					preventLayout=false;
					super.dispatchTouchEvent(ev);
				}
				break;
			case MotionEvent.ACTION_CANCEL:
				touched=false;
				preventLayout=false;
		}
		return true;
	}
	int willScroll(int  scrollYWill){
		final int ds=getCount();
		int vs=getChildCount();
		inertiaLim=0;
		outmost:
		for(;;){
			if(vs>1){
				View first=getChildAt(0);
				int ft=first.getTop();
				int fb=first.getBottom();
				if(ft>scrollYWill){
					View last=getChildAt(vs-1);
					int lt=last.getTop();
					if(lt>=scrollYWill+vh){
						if(position>0){
							removeViewInLayout(last);
							View got=getView(last,position-1);
							addView(got,0);
							measure(got);
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							got.layout(0,ft-mh,mw,ft);
							position--;
						}else{
							scrollYWill=ft;
							inertiaLim=1;
						}
					}else if(position>0){
						View got;
						if(position+vs<ds){
							if(excess.isEmpty()){
								got=getView(null,--position);
							}else{
								got=getView(excess.pop(),--position);
							}
							measure(got);
							addView(got,0);
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							got.layout(0,ft-mh,mw,ft);
							vs++;
						}else{
							int lb=last.getBottom();
							if(lb<scrollYWill+vh){
								scrollYWill=lb-vh;
								inertiaLim=2;
							}else{
								if(excess.isEmpty()){
									got=getView(null,--position);
								}else{
									got=getView(excess.pop(),--position);
								}
								measure(got);
								addView(got,0);
								int mw=got.getMeasuredWidth();
								int mh=got.getMeasuredHeight();
								got.layout(0,ft-mh,mw,ft);
								vs++;
							}
						}
					}else{
						scrollYWill=ft;
						inertiaLim=1;
					}
				}else if(fb<=scrollYWill){
					View last=getChildAt(vs-1);
					int lb=last.getBottom();
					if(lb<scrollYWill+vh){
						if(position+vs<ds){
							View got=getView(first,position+vs);
							measure(got);
							removeViewInLayout(first);
							addView(got,vs-1);
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							got.layout(0,lb,mw,lb+mh);
							position++;
						}else if(position>0){
							scrollYWill=lb-vh;
							inertiaLim=2;
						}else{
							int ch=lb-ft;
							if(ch<=vh){
								scrollYWill=ft;
								inertiaLim=1;
							}else{
								scrollYWill=lb-vh;
								inertiaLim=2;
							}
						}
					}else{
						removeViewInLayout(first);
						position++;
						vs--;
						excess.add(first);
					}
				}else{
					for(;vs>0;){
						View last=getChildAt(vs-1);
						int lt=last.getTop();
						int lb=last.getBottom();
						if(lt>=scrollYWill+vh){
							removeViewInLayout(last);
							excess.add(last);
							vs--;
						}else if(lb<scrollYWill+vh){
							if(position+vs<ds){
								View got;
								if(excess.isEmpty()){
									got=getView(null,position+vs);
								}else{
									got=getView(excess.pop(),position+vs);
								}
								measure(got);
								addView(got,vs);
								int mw=got.getMeasuredWidth();
								int mh=got.getMeasuredHeight();
								got.layout(0,lb,mw,lb+mh);
								vs++;
							}else if(position>0){
								scrollYWill=lb-vh;
								inertiaLim=2;
								break;
							}else{
								int ch=lb-ft;
								if(ch<=vh){
									scrollYWill=ft;
									inertiaLim=1;
								}else{
									scrollYWill=lb-vh;
									inertiaLim=2;
								}
								break;
							}
						}else{
							break;
						}
					}
					break;
				}
			}else if(vs==1){
				View first=getChildAt(0);
				int ft=first.getTop();
				//int fb=first.getBottom();
				if(ft>scrollYWill){
					if(position>0){
						View got=getView(null,--position);
						measure(got);
						addView(got,0);
						vs++;
						int mw=got.getMeasuredWidth();
						int mh=got.getMeasuredHeight();
						got.layout(0,ft-mh,mw,ft);
					}else{
						scrollYWill=ft;
						inertiaLim=1;
						if(position+1<ds){
							View got=getView(null,position+1);
							measure(got);
							addView(got,vs);
							vs++;
							int mw=got.getMeasuredWidth();
							int mh=got.getMeasuredHeight();
							got.layout(0,ft-mh,mw,ft);
						}
					}
				}else{
					if(position+1<ds){
						View got=getView(null,position+1);
						measure(got);
						addView(got,vs);
						vs++;
						int mw=got.getMeasuredWidth();
						int mh=got.getMeasuredHeight();
						got.layout(0,ft-mh,mw,ft);
					}else{
						scrollYWill=ft;
						inertiaLim=1;
						break;
					}
				}
			}else{
				break;
			}
		}
		setScrollY(scrollYWill);
		return scrollYWill;
	}
	class InertiaScroll extends Thread{
		int orientation;
		double initialSpeed;
		double minSpeed=0.008;
		double inertia;
		int initialScrollX;
		int initialScrollY;
		InertiaScroll(double speed){
			inertia=0.008;//(10*ma.dp)/1000;
			this.initialSpeed=speed;
		}
		final int sleep=4;
		@Override
		public void run(){
			super.run();
			long initialTime=System.currentTimeMillis();
			long dTime;
			preventLayout=true;
			if(orientation==0){
				int scrollXWill;
				int scrollXSaved=initialScrollX;
				if(initialSpeed<0){
					//log.alert("<0");
					try{
						sleep(1);
						while(!interrupted()){
							if(inertiaLim==0)break;
							dTime=System.currentTimeMillis()-initialTime;
							double speedNow=initialSpeed+inertia*dTime;
							if(speedNow<-minSpeed){
								//scrollXWill=initialScrollX-(int)(initialSpeed*dTime+inertia*dTime*dTime/2);
								scrollXWill=initialScrollX-(int)((initialSpeed+(inertia*dTime)/2)*dTime);
								if(scrollXWill>scrollXSaved){
									post(new LetScroll(scrollXWill,initialScrollY));
								}
								scrollXSaved=scrollXWill;
							}else{
								break;
							}
							sleep(sleep);
						}
					}catch(Exception e){}
				}else if(initialSpeed>0){
					try{
						sleep(1);
						while(!interrupted()){
							dTime=System.currentTimeMillis()-initialTime;
							double speedNow=initialSpeed-inertia*dTime;
							if(speedNow>minSpeed){
								scrollXWill=initialScrollX-(int)(initialSpeed*dTime-inertia*dTime*dTime/2);
								//scrollXWill=initialScrollX-(int)((initialSpeed-(inertia*dTime)/2)*dTime);
								if(scrollXWill<scrollXSaved){
									if(scrollXWill<=0){
										post(new LetScroll(0,initialScrollY));
										return;
									}
									post(new LetScroll(scrollXWill,initialScrollY));
								}
								scrollXSaved=scrollXWill;
							}else{
								break;
							}
							sleep(sleep);
						}
					}catch(Exception e){}
				}else{
					return;
				}
			}else if(orientation==1){
				int scrollYSaved=initialScrollY;
				if(initialSpeed<0){
					try{
						sleep(1);
						while(!interrupted()){
							dTime=System.currentTimeMillis()-initialTime;
							double speedNow=initialSpeed+inertia*dTime;
							if(speedNow<-minSpeed){
								//int scrollYWill=initialScrollY-(int)(initialSpeed*dTime+inertia*dTime*dTime/2);
								int scrollYWill=initialScrollY-(int)((initialSpeed+(inertia*dTime)/2)*dTime);
								if(scrollYWill>scrollYSaved){
									//if(!MyListView.this.isDirty())
										post(new LetScroll(initialScrollX,scrollYWill));
								}
								scrollYSaved=scrollYWill;
							}else{
								break;
							}
							//sleep(sleep);
						}
					}catch(Exception e){}
				}else if(initialSpeed>0){
					try{
						sleep(1);
						while(!interrupted()){
							dTime=System.currentTimeMillis()-initialTime;
							double speedNow=initialSpeed-inertia*dTime;
							if(speedNow>minSpeed){
								int scrollYWill=initialScrollY-(int)((initialSpeed-(inertia*dTime)/2)*dTime);
								if(scrollYWill<scrollYSaved){
									//if(!MyListView.this.isDirty())
										post(new LetScroll(initialScrollX,scrollYWill));
								}
								scrollYSaved=scrollYWill;
							}else{
								break;
							}
							//sleep(sleep);
						}
					}catch(Exception e){}
				}else return;
			}else return;
			preventLayout=false;
		}
		class LetScroll implements Runnable{
			int x,y;
			LetScroll(int x,int y){
				this.x=x;
				this.y=y;
			}
			@Override
			public void run(){
				int result=willScroll(y);
				if(result!=y)InertiaScroll.this.interrupt();
			}
		}
	}
}
